//
// Copyright (c) 2004-2024 Jaroslaw Kowalski <jaak@jkowalski.net>, Kim Christensen, Julian Verdurmen
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// * Redistributions of source code must retain the above copyright notice,
//   this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
//
// * Neither the name of Jaroslaw Kowalski nor the names of its
//   contributors may be used to endorse or promote products derived from this
//   software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
//

namespace NLog.UnitTests.LayoutRenderers
{
    using System;
    using System.Globalization;
    using NLog.LayoutRenderers;
    using Xunit;

    public class DateTests : NLogTestBase
    {
        [Fact]
        public void DefaultDateTest()
        {
            var logFactory = new LogFactory().Setup().LoadConfigurationFromXml(@"
            <nlog>
                <targets><target name='debug' type='Debug' layout='${date}' /></targets>
                <rules>
                    <logger name='*' minlevel='Debug' writeTo='debug' />
                </rules>
            </nlog>").LogFactory;

            var ei = new LogEventInfo(LogLevel.Info, "logger", "msg");
            logFactory.GetLogger("d").Debug(ei);
            DateTime dt = DateTime.ParseExact(GetDebugLastMessage("debug", logFactory), "yyyy/MM/dd HH:mm:ss.fff", CultureInfo.InvariantCulture);
            DateTime now = ei.TimeStamp;

            Assert.True(Math.Abs((dt - now).TotalSeconds) < 1);
        }

        [Fact]
        public void TimeZoneTest()
        {
            var dateLayoutRenderer = new DateLayoutRenderer();

            dateLayoutRenderer.Format = "yyyy-MM-ddTHH:mmK";

            var logEvent = new LogEventInfo(LogLevel.Info, "logger", "msg");
            var result = dateLayoutRenderer.Render(logEvent);

            var offset = TimeZoneInfo.Local;
            var offset2 = offset.GetUtcOffset(DateTime.Now);

            if (offset2 >= new TimeSpan(0))
            {
                //+00:00, +01:00 etc
                Assert.Contains($"+{offset2.Hours:D2}:{offset2.Minutes:D2}", result);
            }
            else
            {
                //-01:00, etc
                Assert.Contains($"{offset2.Hours:D2}:{offset2.Minutes:D2}", result);
            }
        }

        [Theory]
        [InlineData("R")]
        [InlineData("O")]
        public void UniversalTimeTest(string formatString)
        {
            var orgTimeSource = NLog.Time.TimeSource.Current;

            try
            {
                NLog.Time.TimeSource.Current = new NLog.Time.AccurateLocalTimeSource();

                var dt = new DateLayoutRenderer();
                dt.UniversalTime = true;
                dt.Format = formatString;

                var ei = new LogEventInfo(LogLevel.Info, "logger", "msg");
                Assert.Equal(ei.TimeStamp.ToUniversalTime().ToString(formatString), dt.Render(ei));
            }
            finally
            {
                NLog.Time.TimeSource.Current = orgTimeSource;
            }
        }

        [Theory]
        [InlineData("R")]
        [InlineData("O")]
        public void LocalTimeTest(string formatString)
        {
            var orgTimeSource = NLog.Time.TimeSource.Current;

            try
            {
                NLog.Time.TimeSource.Current = new NLog.Time.AccurateUtcTimeSource();

                var dt = new DateLayoutRenderer();
                dt.UniversalTime = false;
                dt.Format = formatString;

                var ei = new LogEventInfo(LogLevel.Info, "logger", "msg");
                Assert.Equal(ei.TimeStamp.ToLocalTime().ToString(formatString), dt.Render(ei));
            }
            finally
            {
                NLog.Time.TimeSource.Current = orgTimeSource;
            }
        }

        [Theory]
        [InlineData("R")]
        [InlineData("O")]
        public void DefaultTimeTest(string formatString)
        {
            var orgTimeSource = NLog.Time.TimeSource.Current;

            try
            {
                NLog.Time.TimeSource.Current = new NLog.Time.AccurateUtcTimeSource();

                var dt = new DateLayoutRenderer();
                dt.Format = formatString;

                var ei = new LogEventInfo(LogLevel.Info, "logger", "msg");

                Assert.Equal(ei.TimeStamp.ToString(formatString), dt.Render(ei));
            }
            finally
            {
                NLog.Time.TimeSource.Current = orgTimeSource;
            }
        }

        [Fact]
        public void DateFormatExplicitTest()
        {
            var logFactory = new LogFactory().Setup().LoadConfigurationFromXml(@"
            <nlog>
                <targets><target name='debug' type='Debug' layout='${date:format=yyyy-MM-dd}' /></targets>
                <rules>
                    <logger name='*' minlevel='Debug' writeTo='debug' />
                </rules>
            </nlog>").LogFactory;

            logFactory.GetLogger("d").Debug("zzz");
            logFactory.AssertDebugLastMessage(DateTime.Now.ToString("yyyy-MM-dd"));
        }

        [Fact]
        public void DateFormatDefaultTest()
        {
            var logFactory = new LogFactory().Setup().LoadConfigurationFromXml(@"
            <nlog>
                <targets><target name='debug' type='Debug' layout='${date:\thh\:mm\:ss:UniversalTime=true}' /></targets>
                <rules>
                    <logger name='*' minlevel='Debug' writeTo='debug' />
                </rules>
            </nlog>").LogFactory;

            var dateTimeUtc = DateTime.UtcNow;
            logFactory.GetLogger("d").Log(new LogEventInfo(LogLevel.Info, null, "Hello") { TimeStamp = dateTimeUtc });
            logFactory.AssertDebugLastMessage(dateTimeUtc.ToString("\thh:mm:ss", CultureInfo.InvariantCulture));
        }

        [Fact]
        public void DateFormatInvalidTest()
        {
            Assert.Throws<NLogConfigurationException>(() =>
            {
                new LogFactory().Setup().LoadConfigurationFromXml(@"
                    <nlog throwConfigExceptions='true'>
                        <variable name='logDate' value='${date:format=hh:mm}' />
                    </nlog>");
            });
        }
    }
}
